<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>函数</title>
</head>

<body>
    <script>
        // 嵌套函数，重复的过程要提炼出函数
        function hypotenuse(a, b) {
            var square = function (x) {
                return x * x;
            }
            return Math.sqrt(square(a) + square(b));
        }
        hypotenuse(3, 4);
        console.log(hypotenuse(3, 4));

        // 函数的调用方式
        // 1.函数的方式
        // 2.作为方法
        var objFun = {
            add: function (a, b) {
                return a + b;
            }
        };

        var obj = {};
        obj.add = function (a, b) {
            return a + b;
        }
        console.log(obj.add(1, 2));

        // 3.构造函数
        var arr = new Array();

        // 4.间接调用
        // 间接调用和直接调用，唯一的区别是可以绑定this指针。
        // 如果不考虑this,这三种调用方式完全一样。

        function hypotenuse(a, b) {
            return Math.sqrt(a * a + b * b);
        }

        this.hypotenuse(3, 4);
        console.log(this.hypotenuse(3, 4)); // 5
        hypotenuse.call(this, 3, 4);
        console.log(hypotenuse.call(this, 3, 4));
        hypotenuse.apply(this, [3, 4]);
        console.log('--------------------');


        // 形参和实参
        // 形参和实参是不一样的，数量，类型都可以不一样，JS engine doesn't carejs引擎不在乎
        // 形参和实参会绑定，动态关联。
        // 形参就是函数声明时的变量，实参是我们调用该函数时传入的具体参数。

        function foo(a, b, c) { // a, b, c 是形参
            console.log(foo.length);
            console.log(arguments); // arguments 是实参数组

            arguments[1] = -1;
            console.log(b);
            console.log(arguments);
            console.log(foo.length);
        }

        foo(1);
        foo(1, 2, 3, 4, 5, 6);
        console.log('--------------------');


        // 递归

        // function foo(i) {
        //     if(i < 0) return;
        //     console.log('begin: ' + i);
        //     foo(i-1); // foo(1'), foo(0)
        //     console.log('end: ' + i);
        // }

        // foo(2);

        /*
         * 常规写法
         */
        function fibonacci(n) {
            if (n <= 1) {
                return 1;
            }
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
        // console.time('Recursive  call');
        // console.log(fibonacci(40));
        // console.timeEnd('Recursive  call');

        /*
         * 尾调用写法
         */
        function fibonacciTail(n, ac1 = 1, ac2 = 1) {

            if (n <= 1) {
                return ac2;
            }

            return fibonacciTail(n - 1, ac2, ac1 + ac2);
        }
        console.time('tail call');
        console.log(fibonacciTail(7000));
        console.timeEnd('tail call');

        /*
         * 循环写法
         */
        function fibonacciLoop(n) {
            var num1 = num2 = 1;
            while (--n) {
                [num1, num2] = [num2, num1 + num2];
            }

            return num2;
        }
        console.time('loop call');
        console.log(fibonacciLoop(10000000));
        console.timeEnd('loop call');

        // 汉诺塔

        function doHanoi(n, src, dest, hpr) { // src: 源  dest: 目的    hpr:辅助
            if (n > 1) {
                doHanoi(n - 1, src, hpr, dest);
                console.log('Move No.' + n + ' dish from ' + src + ' to ' + dest);
                doHanoi(n - 1, hpr, dest, src);
            } else if (n == 1) {
                console.log('Move No.' + n + ' dish from ' + src + ' to ' + dest);
            } else {
                console.log('Error');
            }
        }

        doHanoi(4, 'A', 'B', 'C');
    </script>
</body>

</html>